package javanet.util;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.ejb.Stateful;
import javax.ejb.Stateless;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import javax.rmi.PortableRemoteObject;


/**
 * @author Markus Holzer
 */
public class BeanLocator
{
	
	public static String moduleName;
	
	private static Map<String, Object> cachedFacadeList = new HashMap<String, Object>();

    //~ Methods ----------------------------------------------------------------

    public static <T> T lookup(Class<T> clazz, String jndiName)
    {
        Object bean = lookup(jndiName);

        return clazz.cast(PortableRemoteObject.narrow(bean, clazz));
    }
    
    public static Object lookupByFacadeName(String facadeName)
    {
    	if (cachedFacadeList.containsKey(facadeName))
    		return cachedFacadeList.get(facadeName);
    	
    	String jndiName = "java:global/" + moduleName + "/"
			+ facadeName;
    	Object object = lookup(jndiName);
    	cachedFacadeList.put(facadeName, object);
    	return object;
    }

    public static Object lookup(String jndiName)
    {
        Context context = null;
        try
        {
            context = new InitialContext();

            return context.lookup(jndiName);
        }
        catch (NamingException ex)
        {
            throw new IllegalStateException("Cannot connect to bean: " + jndiName + " Reason: " + ex, ex.getCause());
        }
        finally
        {
            try
            {
                context.close();
            }
            catch (NamingException ex)
            {
                throw new IllegalStateException("Cannot close InitialContext. Reason: " + ex, ex.getCause());
            }
        }
    }

    //~ Inner Classes ----------------------------------------------------------

    /**
     * TODO: Umstellen, sobald JEE6 verfügbar
     *
     * @author   Edin Pezerovic
     * @version  1.0, $Date$
     */
    public static class GlobalJNDIName
    {

        //~ Static fields/initializers -----------------------------------------

        private static final String PREFIX = "java:global";
        private static final String SEPARATOR = "/";
        private static final String PROPERTY_FILE = "/global.jndi";
        private static final String MODULE_NAME_KEY = "module.name";
        private static final String APP_NAME_KEY = "application.name";

        //~ Instance fields ----------------------------------------------------

        private StringBuilder builder;
        private Properties globalConfiguration;
        private String appName;
        private String moduleName;
        private String beanName;
        private String businessInterfaceName;

        //~ Constructors -------------------------------------------------------

        /**
         * Creates a new GlobalJNDIName object.
         */
        public GlobalJNDIName()
        {
            this.builder = new StringBuilder();
            this.builder.append(PREFIX).append(SEPARATOR);
            this.globalConfiguration = new Properties();
            try
            {
                this.globalConfiguration.load(this.getClass().getResourceAsStream(PROPERTY_FILE));
            }
            catch (Exception ex)
            {
                System.out.println("Cannot load configuration: " + ex);
            }

            this.appName = this.globalConfiguration.getProperty(APP_NAME_KEY);
            this.moduleName = this.globalConfiguration.getProperty(MODULE_NAME_KEY);
        }

        //~ Methods ------------------------------------------------------------

        /**
         * DOCUMENT ME!
         *
         * @param   appName  DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        public GlobalJNDIName withAppName(String appName)
        {
            this.appName = appName;

            return this;
        }

        /**
         * DOCUMENT ME!
         *
         * @param   moduleName  DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        public GlobalJNDIName withModuleName(String moduleName)
        {
            this.moduleName = moduleName;

            return this;
        }

        /**
         * DOCUMENT ME!
         *
         * @param   beanName  DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        public GlobalJNDIName withBeanName(String beanName)
        {
            this.beanName = beanName;

            return this;
        }

        /**
         * DOCUMENT ME!
         *
         * @param   beanClass  DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        public GlobalJNDIName withBeanName(Class<?> beanClass)
        {
            return withBeanName(computeBeanName(beanClass));
        }

        /**
         * DOCUMENT ME!
         *
         * @param   interfaze  DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        public GlobalJNDIName withBusinessInterface(Class<?> interfaze)
        {
            this.businessInterfaceName = interfaze.getName();

            return this;
        }

        /**
         * DOCUMENT ME!
         *
         * @param   beanClass  DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        String computeBeanName(Class<?> beanClass)
        {
            Stateless stateless = beanClass.getAnnotation(Stateless.class);
            if ((stateless != null) && isNotEmpty(stateless.name()))
            {
                return stateless.name();
            }

            Stateful stateful = beanClass.getAnnotation(Stateful.class);
            if ((stateful != null) && isNotEmpty(stateful.name()))
            {
                return stateful.name();
            }

            // TODO: erst mit JEE6
            // Singleton singleton = (Singleton) beanClass.getAnnotation(Singleton.class);
            // if (singleton != null && isNotEmpty(singleton.name())) {
            // return singleton.name();
            // }
            return beanClass.getSimpleName();
        }

        /**
         * DOCUMENT ME!
         *
         * @param   name  DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        private boolean isNotEmpty(String name)
        {
            return ((name != null) && !name.isEmpty());
        }

        /**
         * DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        public String asString()
        {
            if (appName != null)
            {
                this.builder.append(appName).append(SEPARATOR);
            }

            this.builder.append(moduleName).append(SEPARATOR);
            this.builder.append(beanName);
            if (businessInterfaceName != null)
            {
                this.builder.append("#").append(businessInterfaceName);
            }

            return this.builder.toString();
        }

        /**
         * DOCUMENT ME!
         *
         * @param   <T>    DOCUMENT ME!
         * @param   clazz  DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        public <T> T locate(Class<T> clazz)
        {
            return BeanLocator.lookup(clazz, asString());
        }

        /**
         * DOCUMENT ME!
         *
         * @return  DOCUMENT ME!
         */
        public Object locate()
        {
            return BeanLocator.lookup(asString());
        }
    }
}
